{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "0d766f79",
   "metadata": {},
   "source": [
    "## 飞浆设计之道"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f101b17a",
   "metadata": {},
   "source": [
    "飞浆是由百度开源的深度学习框架，模块化的程序开发，让开发者专注于业务而不是代码的华丽。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "639b281b",
   "metadata": {},
   "source": [
    "## 入门案例（波士顿房价预测）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "dcd35224",
   "metadata": {},
   "outputs": [],
   "source": [
    "import paddle\n",
    "from paddle.nn import Linear\n",
    "import paddle.nn.functional as F\n",
    "import numpy as np\n",
    "import os"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "85036c01",
   "metadata": {},
   "source": [
    "### 数据处理"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "ad2f7a4d",
   "metadata": {},
   "outputs": [],
   "source": [
    "def load_data():\n",
    "    # 读取数据\n",
    "    datafile = \"./data/housing.data\"\n",
    "    data = np.fromfile(datafile,sep=' ',dtype=np.float32)\n",
    "    # 特征与预测值划分\n",
    "    feature_names = [ 'CRIM', 'ZN', 'INDUS', 'CHAS', 'NOX', 'RM', 'AGE', \\\n",
    "                      'DIS', 'RAD', 'TAX', 'PTRATIO', 'B', 'LSTAT', 'MEDV' ]\n",
    "    feature_num = len(feature_names)\n",
    "\n",
    "    data = data.reshape([data.shape[0]//feature_num,feature_num])\n",
    "\n",
    "\n",
    "    # 划分测试集与训练集\n",
    "    ratio = 0.8\n",
    "    offset = int(data.shape[0]*ratio)\n",
    "    training_data = data[:offset]\n",
    "    test_data = data[offset:]\n",
    "\n",
    "    # 数据归一化处理\n",
    "    maximnus,minimuns,avgs = training_data.max(axis=0),training_data.min(axis=0),training_data.mean(axis=0)\n",
    "    for i in range(feature_num):\n",
    "        data[:,i] = (data[:,i]-avgs[i])/(maximnus[i]-minimuns[i])\n",
    "    return training_data,test_data,maximnus,minimuns,avgs"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3c5d680f",
   "metadata": {},
   "source": [
    "### 模型设计"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c338143a",
   "metadata": {},
   "source": [
    "飞浆建议使用python的类设计模型\n",
    "- 在init中继承paddle.nn.Layer，编写网络\n",
    "- 在forward中实现前向传播算法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "45a01e11",
   "metadata": {},
   "outputs": [],
   "source": [
    "class Regressor(paddle.nn.Layer):\n",
    "    def __init__(self):\n",
    "        # 继承父类中的属性及方法\n",
    "        super(Regressor,self).__init__()\n",
    "        # 定义一层全连接层\n",
    "        self.fc = Linear(in_features=13,out_features=1)\n",
    "    \n",
    "    def forward(self,inputs):\n",
    "        x = self.fc(inputs)\n",
    "        return x"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c6720a9b",
   "metadata": {},
   "source": [
    "### 训练配置\n",
    "- 制定运行代码的机器及配置\n",
    "- 实例化模型\n",
    "- 加载训练数据和测试数据\n",
    "- 设置优化算法及学习率"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "bd6339e0",
   "metadata": {},
   "outputs": [],
   "source": [
    "model = Regressor()\n",
    "model.train()\n",
    "\n",
    "training_data,test_data,maximnus,minimuns,avgs = load_data()\n",
    "\n",
    "opt = paddle.optimizer.SGD(learning_rate=0.01,parameters=model.parameters())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f169facb",
   "metadata": {},
   "source": [
    "### 训练过程\n",
    "在训练时，需要将数据拆分为多份，每次训练一小部分，训练过程分为2个循环\n",
    "- 第一个循环控制训练次数\n",
    "- 第二个循环负责将数据拆分为多份训练"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "18aef4eb",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "epoch:0,iter:0,loss is [0.21894832]\n",
      "epoch:0,iter:20,loss is [0.14168386]\n",
      "epoch:0,iter:40,loss is [0.26931584]\n",
      "epoch:1,iter:0,loss is [0.1260764]\n",
      "epoch:1,iter:20,loss is [0.13441436]\n",
      "epoch:1,iter:40,loss is [0.21419317]\n",
      "epoch:2,iter:0,loss is [0.10274737]\n",
      "epoch:2,iter:20,loss is [0.1770517]\n",
      "epoch:2,iter:40,loss is [0.13733743]\n",
      "epoch:3,iter:0,loss is [0.07843805]\n",
      "epoch:3,iter:20,loss is [0.14909606]\n",
      "epoch:3,iter:40,loss is [0.03228609]\n",
      "epoch:4,iter:0,loss is [0.12735952]\n",
      "epoch:4,iter:20,loss is [0.06470194]\n",
      "epoch:4,iter:40,loss is [0.04476946]\n"
     ]
    }
   ],
   "source": [
    "EPOCH_NUM = 5\n",
    "BATCH_SIZE = 10\n",
    "\n",
    "for epoch_id in range(EPOCH_NUM):\n",
    "    np.random.shuffle(training_data)\n",
    "    mini_batches = [training_data[k:k+BATCH_SIZE] for k in range(0,len(training_data),BATCH_SIZE)]\n",
    "    for iter_id,mini_batch in enumerate(mini_batches):\n",
    "        x = np.array(mini_batch[:,:-1])\n",
    "        y = np.array(mini_batch[:,-1])\n",
    "        house_features = paddle.to_tensor(x)\n",
    "        prices = paddle.to_tensor(y)\n",
    "        # 前向算法\n",
    "        predicts = model.forward(house_features)\n",
    "        # 计算损失\n",
    "        loss = F.square_error_cost(predicts,label=prices)\n",
    "        avg_loss = paddle.mean(loss)\n",
    "        if iter_id % 20 ==0:\n",
    "            print(\"epoch:{},iter:{},loss is {}\".format(epoch_id,iter_id,avg_loss.numpy()))\n",
    "        # 反向传播\n",
    "        avg_loss.backward()\n",
    "        # 最小化loss，更新参数\n",
    "        opt.step()\n",
    "        # 清除梯度\n",
    "        opt.clear_grad()\n",
    "    \n",
    "    "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3a2d3012",
   "metadata": {},
   "source": [
    "### 保存并测试模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "62ad0d4b",
   "metadata": {},
   "outputs": [],
   "source": [
    "paddle.save(model.state_dict(),'./pdparams/LR_model.pdparams')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "634049bb",
   "metadata": {},
   "outputs": [],
   "source": [
    "def load_one_example():\n",
    "    idx = np.random.randint(0,test_data.shape[0])\n",
    "    one_data,label = test_data[idx,:-1],test_data[idx,-1]\n",
    "    one_data = one_data.reshape([1,-1])\n",
    "    return one_data,label"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "86ef16ba",
   "metadata": {},
   "outputs": [],
   "source": [
    "model_dict = paddle.load(\"./pdparams/LR_model.pdparams\")\n",
    "model.load_dict(model_dict)\n",
    "model.eval()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "9d08eeb0",
   "metadata": {},
   "outputs": [],
   "source": [
    "one_data,label = load_one_example()\n",
    "one_data = paddle.to_tensor(one_data)\n",
    "predict = model(one_data)\n",
    "predict = predict*(maximnus[-1]-minimuns[-1])+avgs[-1]\n",
    "label = label*(maximnus[-1]-minimuns[-1])+avgs[-1]\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "4fe01a44",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Inference result is [[36.62644]], the corresponding label is 8.799999237060547\n"
     ]
    }
   ],
   "source": [
    "print(\"Inference result is {}, the corresponding label is {}\".format(predict.numpy(), label))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b7f2804f",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.8"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": true
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
